1 module hip.graphics.g2d.particles;
2 import hip.math.vector;
3 import hip.api.graphics.color;
4 
5 struct HipParticle
6 {
7     Vector2 initPosition;
8     Vector2 initVelocity;
9     Vector2 initAcceleration;
10     float initAngle = 0;
11     float initScale = 0;
12     float timeStamp = 0;
13 }
14 struct ValueRange
15 {
16     float min = 0, max = 0;
17 
18     float rnd()
19     {
20         import hip.math.random;
21         return Random.rangef(min, max);
22     }
23 }
24 
25 struct HipParticleSystemConfig
26 {
27     ///Means a variating initial value (random)
28     
29     ValueRange scaleInit = ValueRange(1,1);
30     ValueRange scaleEnd = ValueRange(0,0);
31     ValueRange velocityXInit = ValueRange(0,0);
32     ValueRange velocityYInit = ValueRange(0,0);
33     ///In which angle will apply the acceleration
34     ValueRange angleInit = ValueRange(0,0);
35 
36     ValueRange accelerationXInit = ValueRange(0,0);
37     ValueRange accelerationYInit = ValueRange(0,0);
38 
39     ///In which rotation will init.
40     ValueRange rotationInit = ValueRange(0,0);
41     ///Default color stop is to go from opaque white to transparent white.
42     immutable DefaultParticleColorStops = [HipColorStop(HipColor.white, 0), HipColorStop(HipColor(255,255,255,0), 1)];
43 
44     HipColorStop[] colors = DefaultParticleColorStops;
45     float lifeTime = 2.0;
46 }
47 
48 /** 
49  * 2D Particle System 
50  */
51 class HipParticleSystem
52 {
53     HipParticle[] particles;
54     HipParticleSystemConfig config;
55     float currentTime = 0;
56     ///How many particles to spawn per second.
57     float emissionRate = 200;
58     ///Will never spawn more than that value
59     uint maxActive = 500;
60     ///Stores how many particles to spawn, accumulates when not integer.
61     protected float spawnAccumulator = 0;
62     ///Particles to iterate
63     protected uint active;
64 
65     this(uint maxParticles)
66     {
67         particles = new HipParticle[](maxParticles);
68         active = 0;
69     }
70 
71     protected void killParticle(uint id)
72     {
73         assert(id < particles.length, "Particle out of range");
74         HipParticle temp = particles[id];
75         --active;
76         particles[id] = particles[active];
77         particles[active] = temp;   
78     }
79 
80     struct EmissionZone
81     {
82         ValueRange x, y;
83     }
84     EmissionZone emissionZone;
85 
86     void setEmissionZone(int minX, int maxX, int minY, int maxY)
87     {
88         emissionZone =  EmissionZone(ValueRange(minX, maxX), ValueRange(minY, maxY));
89     }
90 
91     void spawnParticles(uint count)
92     {
93         uint i = 0;
94         uint newActive = active;
95         ulong max = particles.length;
96         while(i < count && newActive < max)
97         {
98             //Maybe here should have an initialize particle function
99 
100             particles[newActive] = HipParticle(
101                 Vector2(emissionZone.x.rnd, emissionZone.y.rnd), 
102                 Vector2(config.velocityXInit.rnd, config.velocityYInit.rnd),
103                 Vector2(config.accelerationXInit.rnd, config.accelerationYInit.rnd),
104                 config.angleInit.rnd(),
105                 config.scaleInit.rnd(),
106                 currentTime
107             );
108             newActive++;
109             i++;
110         }
111 
112         active = newActive;
113     }
114 
115     void update(float dt)
116     {
117         currentTime+= dt;
118         uint currActive = active;
119         for(uint i = 0; i < currActive; i++)
120         {
121             HipParticle* p = &particles[i];
122             if(currentTime - p.timeStamp >= config.lifeTime)
123                 killParticle(i);
124         }
125         spawnAccumulator+= dt*emissionRate;
126         spawnParticles(cast(uint)spawnAccumulator);
127         spawnAccumulator-= cast(uint)spawnAccumulator;
128     }
129 
130     void draw()
131     {
132         import hip.graphics.g2d.renderer2d;
133         import hip.math.utils;
134         float invLifetime = 1.0f / config.lifeTime;
135         for(uint i = 0; i < active; i++)
136         {
137             HipParticle* p = &particles[i];
138             float t = (currentTime - p.timeStamp) * invLifetime;
139 
140             Vector2 partPos = p.initPosition + p.initVelocity*t + p.initAcceleration*0.5*t*t;
141             HipColor partColor = config.colors.gradientColor(t);
142 
143             float scale = lerp(p.initScale, config.scaleEnd.max, t);
144             drawTexture(null, cast(int)partPos.x, cast(int)partPos.y, 0, partColor, scale, scale);
145         }
146     }
147 }   
148 
149 
150 class HipParticleSystemDOD
151 {
152     float[] accelerations;
153     float[] velocities;
154     float[] positions;
155     HipColor[] colors;
156 
157 }